package config

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

// TestNewConfig 测试配置创建
func TestNewConfig(t *testing.T) {
	config := NewConfig()
	
	if config == nil {
		t.Fatal("NewConfig() returned nil")
	}
	
	// 验证默认值
	if config.BatchSize <= 0 {
		t.Errorf("Expected positive BatchSize, got %d", config.BatchSize)
	}
	
	if config.MaxRetries < 0 {
		t.Errorf("Expected non-negative MaxRetries, got %d", config.MaxRetries)
	}
	
	if config.RetryDelay <= 0 {
		t.Errorf("Expected positive RetryDelay, got %v", config.RetryDelay)
	}
	
	if config.ConnectionTimeout <= 0 {
		t.Errorf("Expected positive ConnectionTimeout, got %v", config.ConnectionTimeout)
	}
	
	if config.QueryTimeout <= 0 {
		t.Errorf("Expected positive QueryTimeout, got %v", config.QueryTimeout)
	}
	
	if config.MaxConnections <= 0 {
		t.Errorf("Expected positive MaxConnections, got %d", config.MaxConnections)
	}
	
	if config.LogLevel == "" {
		t.Error("Expected non-empty LogLevel")
	}
}

// TestConfigValidation 测试配置验证
func TestConfigValidation(t *testing.T) {
	tests := []struct {
		name        string
		modifyFunc  func(*Config)
		shouldError bool
		errorMsg    string
	}{
		{
			name: "Valid config",
			modifyFunc: func(c *Config) {
				// 保持默认值，应该有效
			},
			shouldError: false,
		},
		{
			name: "Invalid batch size - zero",
			modifyFunc: func(c *Config) {
				c.BatchSize = 0
			},
			shouldError: true,
			errorMsg:    "batch size",
		},
		{
			name: "Invalid batch size - negative",
			modifyFunc: func(c *Config) {
				c.BatchSize = -1
			},
			shouldError: true,
			errorMsg:    "batch size",
		},
		{
			name: "Invalid max retries - negative",
			modifyFunc: func(c *Config) {
				c.MaxRetries = -1
			},
			shouldError: true,
			errorMsg:    "max retries",
		},
		{
			name: "Invalid retry delay - zero",
			modifyFunc: func(c *Config) {
				c.RetryDelay = 0
			},
			shouldError: true,
			errorMsg:    "retry delay",
		},
		{
			name: "Invalid connection timeout - zero",
			modifyFunc: func(c *Config) {
				c.ConnectionTimeout = 0
			},
			shouldError: true,
			errorMsg:    "connection timeout",
		},
		{
			name: "Invalid query timeout - zero",
			modifyFunc: func(c *Config) {
				c.QueryTimeout = 0
			},
			shouldError: true,
			errorMsg:    "query timeout",
		},
		{
			name: "Invalid max connections - zero",
			modifyFunc: func(c *Config) {
				c.MaxConnections = 0
			},
			shouldError: true,
			errorMsg:    "max connections",
		},
		{
			name: "Invalid log level - empty",
			modifyFunc: func(c *Config) {
				c.LogLevel = ""
			},
			shouldError: true,
			errorMsg:    "log level",
		},
		{
			name: "Invalid log level - unknown",
			modifyFunc: func(c *Config) {
				c.LogLevel = "INVALID"
			},
			shouldError: true,
			errorMsg:    "log level",
		},
		{
			name: "Empty database host",
			modifyFunc: func(c *Config) {
				c.Database.Host = ""
			},
			shouldError: true,
			errorMsg:    "database host",
		},
		{
			name: "Invalid database port - zero",
			modifyFunc: func(c *Config) {
				c.Database.Port = 0
			},
			shouldError: true,
			errorMsg:    "database port",
		},
		{
			name: "Invalid database port - too high",
			modifyFunc: func(c *Config) {
				c.Database.Port = 70000
			},
			shouldError: true,
			errorMsg:    "database port",
		},
		{
			name: "Empty database name",
			modifyFunc: func(c *Config) {
				c.Database.Name = ""
			},
			shouldError: true,
			errorMsg:    "database name",
		},
		{
			name: "Empty database user",
			modifyFunc: func(c *Config) {
				c.Database.User = ""
			},
			shouldError: true,
			errorMsg:    "database user",
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := NewConfig()
			
			// 设置有效的数据库配置作为基础
			config.Database.Host = "localhost"
			config.Database.Port = 5432
			config.Database.Name = "testdb"
			config.Database.User = "testuser"
			config.Database.Password = "testpass"
			
			// 应用测试修改
			tt.modifyFunc(config)
			
			err := config.Validate()
			
			if tt.shouldError {
				if err == nil {
					t.Errorf("Expected validation error for %s, but got none", tt.name)
					return
				}
				if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(tt.errorMsg)) {
					t.Errorf("Expected error message to contain %q, got: %v", tt.errorMsg, err)
				}
			} else {
				if err != nil {
					t.Errorf("Unexpected validation error for %s: %v", tt.name, err)
				}
			}
		})
	}
}

// TestConfigFromFile 测试从文件加载配置
func TestConfigFromFile(t *testing.T) {
	// 创建临时配置文件
	tempDir := t.TempDir()
	configFile := filepath.Join(tempDir, "config.yaml")
	
	configContent := `
database:
  host: "test-host"
  port: 5433
  name: "test-db"
  user: "test-user"
  password: "test-pass"
  ssl_mode: "require"

batch_size: 2000
max_retries: 5
retry_delay: "2s"
connection_timeout: "15s"
query_timeout: "45s"
max_connections: 15
log_level: "debug"
verbose: true
dry_run: true
`
	
	err := os.WriteFile(configFile, []byte(configContent), 0644)
	if err != nil {
		t.Fatalf("Failed to create test config file: %v", err)
	}
	
	// 从文件加载配置
	config, err := LoadConfigFromFile(configFile)
	if err != nil {
		t.Fatalf("LoadConfigFromFile failed: %v", err)
	}
	
	// 验证配置值
	if config.Database.Host != "test-host" {
		t.Errorf("Expected host 'test-host', got %q", config.Database.Host)
	}
	if config.Database.Port != 5433 {
		t.Errorf("Expected port 5433, got %d", config.Database.Port)
	}
	if config.Database.Name != "test-db" {
		t.Errorf("Expected name 'test-db', got %q", config.Database.Name)
	}
	if config.Database.User != "test-user" {
		t.Errorf("Expected user 'test-user', got %q", config.Database.User)
	}
	if config.Database.Password != "test-pass" {
		t.Errorf("Expected password 'test-pass', got %q", config.Database.Password)
	}
	if config.Database.SSLMode != "require" {
		t.Errorf("Expected ssl_mode 'require', got %q", config.Database.SSLMode)
	}
	
	if config.BatchSize != 2000 {
		t.Errorf("Expected batch_size 2000, got %d", config.BatchSize)
	}
	if config.MaxRetries != 5 {
		t.Errorf("Expected max_retries 5, got %d", config.MaxRetries)
	}
	if config.RetryDelay != 2*time.Second {
		t.Errorf("Expected retry_delay 2s, got %v", config.RetryDelay)
	}
	if config.ConnectionTimeout != 15*time.Second {
		t.Errorf("Expected connection_timeout 15s, got %v", config.ConnectionTimeout)
	}
	if config.QueryTimeout != 45*time.Second {
		t.Errorf("Expected query_timeout 45s, got %v", config.QueryTimeout)
	}
	if config.MaxConnections != 15 {
		t.Errorf("Expected max_connections 15, got %d", config.MaxConnections)
	}
	if config.LogLevel != "debug" {
		t.Errorf("Expected log_level 'debug', got %q", config.LogLevel)
	}
	if !config.Verbose {
		t.Error("Expected verbose to be true")
	}
	if !config.DryRun {
		t.Error("Expected dry_run to be true")
	}
}

// TestConfigFromFileNotFound 测试文件不存在的情况
func TestConfigFromFileNotFound(t *testing.T) {
	nonExistentFile := "/path/that/does/not/exist/config.yaml"
	
	_, err := LoadConfigFromFile(nonExistentFile)
	if err == nil {
		t.Error("Expected error when loading non-existent config file")
	}
}

// TestConfigFromInvalidFile 测试无效配置文件
func TestConfigFromInvalidFile(t *testing.T) {
	tempDir := t.TempDir()
	configFile := filepath.Join(tempDir, "invalid_config.yaml")
	
	// 创建无效的YAML文件
	invalidContent := `
database:
  host: "test-host"
  port: "invalid-port"  # 应该是数字
  invalid_yaml: [
`
	
	err := os.WriteFile(configFile, []byte(invalidContent), 0644)
	if err != nil {
		t.Fatalf("Failed to create invalid config file: %v", err)
	}
	
	_, err = LoadConfigFromFile(configFile)
	if err == nil {
		t.Error("Expected error when loading invalid config file")
	}
}

// TestConfigEnvironmentOverrides 测试环境变量覆盖
func TestConfigEnvironmentOverrides(t *testing.T) {
	// 设置环境变量
	envVars := map[string]string{
		"KWDB_HOST":               "env-host",
		"KWDB_PORT":               "9999",
		"KWDB_DATABASE":           "env-db",
		"KWDB_USER":               "env-user",
		"KWDB_PASSWORD":           "env-pass",
		"KWDB_SSL_MODE":           "disable",
		"KWDB_BATCH_SIZE":         "5000",
		"KWDB_MAX_RETRIES":        "10",
		"KWDB_RETRY_DELAY":        "5s",
		"KWDB_CONNECTION_TIMEOUT": "20s",
		"KWDB_QUERY_TIMEOUT":      "60s",
		"KWDB_MAX_CONNECTIONS":    "25",
		"KWDB_LOG_LEVEL":          "error",
		"KWDB_VERBOSE":            "true",
		"KWDB_DRY_RUN":            "true",
	}
	
	// 设置环境变量
	for key, value := range envVars {
		os.Setenv(key, value)
		defer os.Unsetenv(key)
	}
	
	// 加载配置（应该使用环境变量）
	config := NewConfig()
	err := config.LoadFromEnvironment()
	if err != nil {
		t.Fatalf("LoadFromEnvironment failed: %v", err)
	}
	
	// 验证环境变量覆盖
	if config.Database.Host != "env-host" {
		t.Errorf("Expected host 'env-host', got %q", config.Database.Host)
	}
	if config.Database.Port != 9999 {
		t.Errorf("Expected port 9999, got %d", config.Database.Port)
	}
	if config.Database.Name != "env-db" {
		t.Errorf("Expected name 'env-db', got %q", config.Database.Name)
	}
	if config.Database.User != "env-user" {
		t.Errorf("Expected user 'env-user', got %q", config.Database.User)
	}
	if config.Database.Password != "env-pass" {
		t.Errorf("Expected password 'env-pass', got %q", config.Database.Password)
	}
	if config.Database.SSLMode != "disable" {
		t.Errorf("Expected ssl_mode 'disable', got %q", config.Database.SSLMode)
	}
	
	if config.BatchSize != 5000 {
		t.Errorf("Expected batch_size 5000, got %d", config.BatchSize)
	}
	if config.MaxRetries != 10 {
		t.Errorf("Expected max_retries 10, got %d", config.MaxRetries)
	}
	if config.RetryDelay != 5*time.Second {
		t.Errorf("Expected retry_delay 5s, got %v", config.RetryDelay)
	}
	if config.ConnectionTimeout != 20*time.Second {
		t.Errorf("Expected connection_timeout 20s, got %v", config.ConnectionTimeout)
	}
	if config.QueryTimeout != 60*time.Second {
		t.Errorf("Expected query_timeout 60s, got %v", config.QueryTimeout)
	}
	if config.MaxConnections != 25 {
		t.Errorf("Expected max_connections 25, got %d", config.MaxConnections)
	}
	if config.LogLevel != "error" {
		t.Errorf("Expected log_level 'error', got %q", config.LogLevel)
	}
	if !config.Verbose {
		t.Error("Expected verbose to be true")
	}
	if !config.DryRun {
		t.Error("Expected dry_run to be true")
	}
}

// TestConfigConnectionString 测试连接字符串生成
func TestConfigConnectionString(t *testing.T) {
	config := NewConfig()
	config.Database.Host = "localhost"
	config.Database.Port = 5432
	config.Database.Name = "testdb"
	config.Database.User = "testuser"
	config.Database.Password = "testpass"
	config.Database.SSLMode = "require"
	
	connStr := config.GetConnectionString()
	
	// 验证连接字符串包含必要的组件
	expectedComponents := []string{
		"host=localhost",
		"port=5432",
		"dbname=testdb",
		"user=testuser",
		"password=testpass",
		"sslmode=require",
	}
	
	for _, component := range expectedComponents {
		if !strings.Contains(connStr, component) {
			t.Errorf("Connection string should contain %q, got: %s", component, connStr)
		}
	}
}

// TestConfigConnectionStringWithoutPassword 测试不包含密码的连接字符串
func TestConfigConnectionStringWithoutPassword(t *testing.T) {
	config := NewConfig()
	config.Database.Host = "localhost"
	config.Database.Port = 5432
	config.Database.Name = "testdb"
	config.Database.User = "testuser"
	config.Database.Password = "secretpass"
	config.Database.SSLMode = "require"
	
	connStr := config.GetConnectionStringWithoutPassword()
	
	// 验证连接字符串不包含密码
	if strings.Contains(connStr, "secretpass") {
		t.Errorf("Connection string should not contain password, got: %s", connStr)
	}
	if strings.Contains(connStr, "password=") {
		t.Errorf("Connection string should not contain password field, got: %s", connStr)
	}
	
	// 验证包含其他必要组件
	expectedComponents := []string{
		"host=localhost",
		"port=5432",
		"dbname=testdb",
		"user=testuser",
		"sslmode=require",
	}
	
	for _, component := range expectedComponents {
		if !strings.Contains(connStr, component) {
			t.Errorf("Connection string should contain %q, got: %s", component, connStr)
		}
	}
}

// TestConfigLogLevelValidation 测试日志级别验证
func TestConfigLogLevelValidation(t *testing.T) {
	validLevels := []string{"debug", "info", "warn", "error", "fatal"}
	invalidLevels := []string{"trace", "verbose", "critical", "emergency", ""}
	
	for _, level := range validLevels {
		t.Run("valid_"+level, func(t *testing.T) {
			config := NewConfig()
			config.Database.Host = "localhost"
			config.Database.Port = 5432
			config.Database.Name = "testdb"
			config.Database.User = "testuser"
			config.LogLevel = level
			
			err := config.Validate()
			if err != nil {
				t.Errorf("Valid log level %q should not cause validation error: %v", level, err)
			}
		})
	}
	
	for _, level := range invalidLevels {
		t.Run("invalid_"+level, func(t *testing.T) {
			config := NewConfig()
			config.Database.Host = "localhost"
			config.Database.Port = 5432
			config.Database.Name = "testdb"
			config.Database.User = "testuser"
			config.LogLevel = level
			
			err := config.Validate()
			if err == nil {
				t.Errorf("Invalid log level %q should cause validation error", level)
			}
		})
	}
}

// TestConfigTimeoutParsing 测试超时时间解析
func TestConfigTimeoutParsing(t *testing.T) {
	tests := []struct {
		input    string
		expected time.Duration
		valid    bool
	}{
		{"1s", 1 * time.Second, true},
		{"30s", 30 * time.Second, true},
		{"1m", 1 * time.Minute, true},
		{"5m", 5 * time.Minute, true},
		{"1h", 1 * time.Hour, true},
		{"100ms", 100 * time.Millisecond, true},
		{"invalid", 0, false},
		{"", 0, false},
		{"1x", 0, false},
	}
	
	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			duration, err := time.ParseDuration(tt.input)
			
			if tt.valid {
				if err != nil {
					t.Errorf("Expected valid duration for %q, got error: %v", tt.input, err)
					return
				}
				if duration != tt.expected {
					t.Errorf("Expected duration %v for %q, got %v", tt.expected, tt.input, duration)
				}
			} else {
				if err == nil {
					t.Errorf("Expected error for invalid duration %q, but got none", tt.input)
				}
			}
		})
	}
}

// TestConfigCopy 测试配置复制
func TestConfigCopy(t *testing.T) {
	original := NewConfig()
	original.Database.Host = "original-host"
	original.Database.Port = 1234
	original.BatchSize = 9999
	original.Verbose = true
	
	copy := original.Copy()
	
	// 验证复制的值
	if copy.Database.Host != original.Database.Host {
		t.Errorf("Expected copied host %q, got %q", original.Database.Host, copy.Database.Host)
	}
	if copy.Database.Port != original.Database.Port {
		t.Errorf("Expected copied port %d, got %d", original.Database.Port, copy.Database.Port)
	}
	if copy.BatchSize != original.BatchSize {
		t.Errorf("Expected copied batch size %d, got %d", original.BatchSize, copy.BatchSize)
	}
	if copy.Verbose != original.Verbose {
		t.Errorf("Expected copied verbose %v, got %v", original.Verbose, copy.Verbose)
	}
	
	// 验证是独立的副本
	copy.Database.Host = "modified-host"
	if original.Database.Host == "modified-host" {
		t.Error("Modifying copy should not affect original")
	}
}

// TestConfigMerge 测试配置合并
func TestConfigMerge(t *testing.T) {
	base := NewConfig()
	base.Database.Host = "base-host"
	base.Database.Port = 5432
	base.BatchSize = 1000
	
	override := &Config{
		Database: DatabaseConfig{
			Host: "override-host",
			// Port 未设置，应该使用base的值
		},
		BatchSize: 2000,
		// 其他字段未设置，应该使用base的值
	}
	
	merged := base.Merge(override)
	
	// 验证合并结果
	if merged.Database.Host != "override-host" {
		t.Errorf("Expected merged host 'override-host', got %q", merged.Database.Host)
	}
	if merged.Database.Port != 5432 {
		t.Errorf("Expected merged port 5432 (from base), got %d", merged.Database.Port)
	}
	if merged.BatchSize != 2000 {
		t.Errorf("Expected merged batch size 2000, got %d", merged.BatchSize)
	}
	
	// 验证原始配置未被修改
	if base.Database.Host != "base-host" {
		t.Error("Base config should not be modified by merge")
	}
	if base.BatchSize != 1000 {
		t.Error("Base config should not be modified by merge")
	}
}

// BenchmarkConfigValidation 配置验证性能测试
func BenchmarkConfigValidation(b *testing.B) {
	config := NewConfig()
	config.Database.Host = "localhost"
	config.Database.Port = 5432
	config.Database.Name = "testdb"
	config.Database.User = "testuser"
	config.Database.Password = "testpass"
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := config.Validate()
		if err != nil {
			b.Fatalf("Validation failed: %v", err)
		}
	}
}

// BenchmarkConnectionStringGeneration 连接字符串生成性能测试
func BenchmarkConnectionStringGeneration(b *testing.B) {
	config := NewConfig()
	config.Database.Host = "localhost"
	config.Database.Port = 5432
	config.Database.Name = "testdb"
	config.Database.User = "testuser"
	config.Database.Password = "testpass"
	config.Database.SSLMode = "require"
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = config.GetConnectionString()
	}
}